using System;
using System.Linq.Expressions;
using BusinessFramework.Authoring;
using BusinessFramework.Formula;

namespace BusinessFramework
{
    public class RuleFactory<T>
    {
        private FormulaCompiler _compiler;
        public FormulaCompiler Compiler
        {
            get
            {
                if (_compiler == null)
                    _compiler = FormulaCompiler.Instance;
                return _compiler;
            }
            set { _compiler = value; }
        }

        public ActionRule<T> CreateActionRule(Expression<Action<T>> action)
        {
            return new ActionRule<T>(action.Compile())
                .WithDependencies(action);
        }

        public ActionRule<T> CreateActionRuleWithoutDependency(Action<T> action)
        {
            return new ActionRule<T>(action);
        }

        public BusinessRule<T, V> CreateBusinessRule<V>(Expression<Func<T, V>> func, Expression<Func<T, V>> target)
        {
            var targetGet = target.Compile();
            var targetSet = DependencyHelper.GetSetFromGet(target);
            var rule = new BusinessRule<T, V>(func.Compile(), targetGet, targetSet);

            rule.WithDependencies(func);
            rule.WithDependencies(target, BusinessRule.TargetChangeTag);

            return rule;
        }

        public BusinessRule CreateBusinessRule(string formula)
        {
            var index = formula.IndexOf("=");
            var left = formula.Substring(0, index);
            var right = formula.Substring(index + 1);

            var get = Compiler.NewLambda()
                .WithThis<T>()
                .Returns<object>()
                .CompileToExpression(left);

            var func = Compiler.NewLambda()
                .WithThis<T>()
                .Returns<object>()
                .CompileToExpression(right);

            if (func == null)
                throw new FormatException(Compiler.BuildInfo.Message);

            return CreateBusinessRule(func, get);
        }

        public ValidationRule<T> CreateValidationRuleWithoutDependency(
            Func<T, bool> predicate)
        {
            return new ValidationRule<T>(predicate);
        }
 
        public ValidationRule<T> CreateValidationRule(Expression<Func<T, bool>> predicate)
        {
            var rule = new ValidationRule<T>(predicate.Compile())
                .WithDependencies(predicate);

            if (rule._propertyDependency != null && rule._propertyDependency.Count == 1)
                rule.WithOwner(string.Join(".", rule._propertyDependency[0].One.ToArray()));
            return rule;
        }

        public ValidationRule<T> CreateValidationRule(string formula)
        {
            var f = Compiler.NewLambda()
                .WithThis<T>()
                .Returns<bool>()
                .CompileToExpression(formula);

            if (f == null)
                throw new FormatException(Compiler.BuildInfo.Message);

            var rule = new ValidationRule<T>(f.Compile())
                .WithDependencies(f);

            if (rule._propertyDependency != null && rule._propertyDependency.Count == 1)
                rule.WithOwner(string.Join(".", rule._propertyDependency[0].One.ToArray()));

            return rule;
        }

        public PredefinedValidationRule<T, TValidation> CreateValidationRule<V, TValidation>(Expression<Func<T, V>> selector, TValidation validation)
            where TValidation : Validation
        {
            var f = selector.Compile();

            var rule = CreateValidationRuleWithoutDependency(f, validation)
                .WithDependencies(selector)
                .WithOwner(selector);

            return rule;
        }

        public PredefinedValidationRule<T, TValidation> CreateValidationRuleWithoutDependency<V, TValidation>(Func<T, V> selector, TValidation validation)
            where TValidation : Validation
        {
            var rule = new PredefinedValidationRule<T,TValidation>((e,v) => v.Validate(selector(e)), validation);

            return rule;
        }

        //public ValidationRule<T> CreateValidationRuleWithoutDependency<V>(Func<T, V> selector, Validation validation)
        //{
        //    var rule = new ValidationRule<T>(e => validation.Validate(selector(e)));

        //    validation.Apply(rule);
        //    return rule;
        //}
    }
}